package org.tinygroup.template.loader;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.tinygroup.fileresolver.FileResolver;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;
import org.tinygroup.template.Template;
import org.tinygroup.template.TemplateEngine;
import org.tinygroup.template.impl.TemplateEngineDefault;
import org.tinygroup.vfs.FileObject;
import org.tinygroup.vfs.FileObjectFilter;
import org.tinygroup.vfs.FileObjectProcessor;
import org.tinygroup.vfs.VFS;

/**
 * 模板文件资源管理器(页面文件、布局文件和宏文件)
 * @author yancheng11334
 *
 */
public class FileResourceManager {

	protected static final Logger LOGGER = LoggerFactory
            .getLogger(FileResourceManager.class);
	
	/**
	 * 文件资源
	 */
	private Map<String,FileObject>  resources = new ConcurrentHashMap<String,FileObject>();
	
	/**
	 * 地址映射
	 */
	private Map<String,List<String>> pathCaches = new ConcurrentHashMap<String,List<String>>();
	
	/**
	 * 文件扫描器
	 */
	private FileResolver fileResolver;
	
	private List<FileObject>  scanFileList = new ArrayList<FileObject>();
	
	public FileResolver getFileResolver() {
		return fileResolver;
	}

	public void setFileResolver(FileResolver fileResolver) {
		this.fileResolver = fileResolver;
	}

	public void addScanFile(FileObject file){
		if(!scanFileList.contains(file)){
			scanFileList.add(file);
		}
	}
	/**
	 * 批量增加某个VFS路径下的模板引擎相关资源
	 * @param engine
	 * @param root
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void addResources(final TemplateEngine engine,final FileObject root,final String templateExtName,final String layoutExtName,final String componentExtName){
		addScanFile(root);
		root.foreach(new FileObjectFilter() {
            public boolean accept(FileObject fileObject) {
            	String name = fileObject.getFileName();
                return  (templateExtName!=null && name.endsWith(templateExtName)) || (layoutExtName!=null && name.endsWith(layoutExtName)) || (componentExtName!=null && name.endsWith(componentExtName));
            }
        }, new FileObjectProcessor() {
            public void process(FileObject fileObject) {
            	LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]开始加载",fileObject.getAbsolutePath());
                try {
                	addResource(fileObject.getPath(), fileObject);
                	Template template = TemplateLoadUtil.loadComponent((TemplateEngineDefault)engine,fileObject);
                	engine.getRepositories().put(fileObject.getPath(), template);
                	if(componentExtName!=null && fileObject.getFileName().endsWith(componentExtName)){
                		engine.registerMacroLibrary(template);
                	}
                } catch (Exception e) {
                	 LOGGER.errorMessage("加载模板资源文件[{0}]出错,注册主键[{1}]", e,fileObject.getAbsolutePath(),fileObject.getPath());
                }
                LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]加载完毕,注册主键[{1}]",fileObject.getAbsolutePath(),fileObject.getPath());
            }
        });
	}
	/**
	 * 批量增加某个VFS路径下的模板引擎相关资源
	 * @param engine
	 * @param resource
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void addResources(final TemplateEngine engine,final String resource,final String templateExtName,final String layoutExtName,final String componentExtName){
		FileObject root = VFS.resolveFile(resource);
		addResources(engine,root,templateExtName,layoutExtName,componentExtName);
	}
	
	/**
	 * 添加文件资源
	 * @param path
	 * @param file
	 */
	public void addResource(String path,FileObject file){
		//处理文件资源
		resources.put(path, file);
		//处理映射表
		String[] dirs = splitPath(path);
		if(dirs!=null && dirs.length>1){
			String newPath = null;
			for(int i=1;i<dirs.length;i++){
				 newPath = mergePath(dirs,i);
				 List<String> paths = pathCaches.get(newPath);
				 if(paths==null){
					paths = new ArrayList<String>();
					pathCaches.put(newPath, paths);
				 }
				 paths.add(path); //将资源表的键值地址存入列表
			}
		}
	}
	
	public void removeScanFile(FileObject file){
		scanFileList.remove(file);
	}
	/**
	 * 批量删除某个VFS路径下的模板引擎相关资源
	 * @param engine
	 * @param root
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void removeResources(final TemplateEngine engine,final FileObject root,final String templateExtName,final String layoutExtName,final String componentExtName){
		removeScanFile(root);
		root.foreach(new FileObjectFilter() {
            public boolean accept(FileObject fileObject) {
            	String name = fileObject.getFileName();
                return  (templateExtName!=null && name.endsWith(templateExtName)) || (layoutExtName!=null && name.endsWith(layoutExtName)) || (componentExtName!=null && name.endsWith(componentExtName));
            }
        }, new FileObjectProcessor() {
            public void process(FileObject fileObject) {
            	LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]开始移除",fileObject.getAbsolutePath());
        		try {
        			removeResource(fileObject.getPath());
        			engine.removeTemplate(fileObject.getPath());
                } catch (Exception e) {
                    LOGGER.errorMessage("移除模板资源文件[{0}]出错,卸载主键[{1}]", e,fileObject.getAbsolutePath(),fileObject.getPath());
                }
        		LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]移除完毕,卸载主键[{1}]",fileObject.getAbsolutePath(),fileObject.getPath());
            }
        });
	}
	/**
	 * 批量删除某个VFS路径下的模板引擎相关资源
	 * @param engine
	 * @param resource
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void removeResources(final TemplateEngine engine,final String resource,final String templateExtName,final String layoutExtName,final String componentExtName){
		FileObject root = VFS.resolveFile(resource);
		removeResources(engine,root,templateExtName,layoutExtName,componentExtName);
	}
	
	/**
	 * 删除文件资源
	 * @param path
	 * @param file
	 */
	public void removeResource(String path){
		//处理文件资源
		resources.remove(path);
		//处理映射表
		String[] dirs = splitPath(path);
		if(dirs!=null && dirs.length>1){
		   String newPath = null;
		   for(int i=1;i<dirs.length;i++){
			   newPath = mergePath(dirs,i); 
			   List<String> paths = pathCaches.get(newPath);
			   if(paths!=null){
				  paths.remove(path);  //删除资源表的键值地址
				  if(paths.isEmpty()){
					 pathCaches.remove(newPath);
				  }
			   }
		   }
		}
	}
	
	/**
	 * 查询已经注册文件资源
	 * @param path
	 * @return
	 */
	public FileObject getFileObject(String path){
		//先直接地址匹配
		if(resources.containsKey(path)){
		   return resources.get(path);
		}
		//再逐个目录删减，从地址映射表查询
		String[] dirs = splitPath(path);
		if(dirs!=null && dirs.length>0){
		   if(dirs.length==1){
			   List<String> paths = pathCaches.get(path);
			   if(paths!=null && paths.size()>0){ //至少匹配一个
				   return resources.get(paths.get(0));
			   }
		   }else{
			   String newPath = null;
			   //查询与创建和删除不一样，遍历从第0个元素开始
			   for(int i=0;i<dirs.length;i++){
				   newPath = mergePath(dirs,i);
				   List<String> paths = pathCaches.get(newPath);
				   if(paths!=null && paths.size()>0){ //至少匹配一个
					   return resources.get(paths.get(0));
				   }
			   }
		   }
		   
		}
		return null;
	}
	
	/**
	 * 查询注册模板资源以外的其他文件
	 * @param path
	 * @return
	 */
	public FileObject getOtherFileObject(String path){
		//通过文件扫描器查询
		if(fileResolver!=null){
			for(String rootPath:fileResolver.getScanningPaths()){
				FileObject root = VFS.resolveFile(rootPath);
				FileObject file = root.getFileObject(path);
				if(file!=null){
				   return file;
				}
			}
		}
		//非文件扫描器方式查询
		for(FileObject root:scanFileList){
			FileObject file = root.getFileObject(path);
			if(file!=null){
			   return file;
			}
		}
		
		return null;
	}
	
	/**
	 * 合并路径
	 * @param dirs
	 * @param n
	 * @return
	 */
	private String mergePath(String[] dirs,int n){
		StringBuilder sb = new StringBuilder();
		for(int i=n;i<dirs.length;i++){
			sb.append("/").append(dirs[i]);
		}
		return sb.toString();
	}
	/**
	 * 拆分路径
	 * @param path
	 * @return
	 */
	private String[] splitPath(String path){
		path = path.startsWith("/")?path.substring(1):path;
		return path.split("/");
	}
	
	/**
	 * 文件是否被修改
	 * @param path
	 * @return
	 */
	public boolean isModified(String path) {
		FileObject fileObject = getFileObject(path);
		if(fileObject==null){
		   return true; 
		}
		return fileObject.isModified();
	}
	
	/**
	 * 重置文件修改标识
	 * @param path
	 */
	public void resetModified(String path) {
		FileObject fileObject = getFileObject(path);
        if (fileObject != null) {
            fileObject.resetModified();
        }
	}
}
